04. DB Testing

Testing MongoDB Code

Let’s see how we can write tests for Spring Data MongoDB application. Just like we had used H2 for testing JPA repositories, we will be leveraging an unofficial embedded MongoDB implementation.

Test Using Embedded MongoDB

This section covers two scenarios

  1. Autoconfigured Spring Boot Test.
  2. Test with custom manual configuration.

For both scenarios, the embedded MongoDB dependency should be added,

<dependency>
    <groupId>de.flapdoodle.embed</groupId>
    <artifactId>de.flapdoodle.embed.mongo</artifactId>
    <scope>test</scope>
</dependency>

It’s scoped to test as we only need it for our tests.

Autoconfigured Spring Boot Test

After adding de.flapdoodle.embed.mongo dependency Spring Boot will automatically try to download and start the embedded MongoDB when running tests.

The package will be downloaded only once for each version so that subsequent tests run much faster.
At this stage we should be able to start and pass the sample JUnit 5 integration test:

@DataMongoTest
@ExtendWith(SpringExtension.class)
public class MongoDbSpringIntegrationTest {
    @DisplayName("given object to save"
        + " when save object using MongoDB template"
        + " then object is saved")
    @Test
    public void test(@Autowired MongoTemplate mongoTemplate) {
        // given
        DBObject objectToSave = BasicDBObjectBuilder.start()
            .add("key", "value")
            .get();

        // when
        mongoTemplate.save(objectToSave, "collection");

        // then
        assertThat(mongoTemplate.findAll(DBObject.class, "collection")).extracting("key")
            .containsOnly("value");
    }
}

When this test is run, something similar to this will be seen in the logs,

...Starting MongoDbSpringIntegrationTest on arroyo with PID 100...
Manual Configuration Test

Spring Boot will automatically start and configure the embedded database and then inject MongoTemplateinstance for us. However, sometimes we might need to configure embedded Mongo database manually(e.g., when testing a specific DB version).

The following snippet shows how we can configure the embedded MongoDB instance manually. This is roughly the equivalent of the previous Spring test:

class ManualEmbeddedMongoDbIntegrationTest {
    private MongodExecutable mongodExecutable;
    private MongoTemplate mongoTemplate;

    @AfterEach
    void clean() {
        mongodExecutable.stop();
    }

    @BeforeEach
    void setup() throws Exception {
        String ip = "localhost";
        int port = 27017;

        IMongodConfig mongodConfig = new MongodConfigBuilder().version(Version.Main.PRODUCTION)
            .net(new Net(ip, port, Network.localhostIsIPv6()))
            .build();

        MongodStarter starter = MongodStarter.getDefaultInstance();
        mongodExecutable = starter.prepare(mongodConfig);
        mongodExecutable.start();
        mongoTemplate = new MongoTemplate(new MongoClient(ip, port), "test");
    }

    @DisplayName("given object to save"
        + " when save object using MongoDB template"
        + " then object is saved")
    @Test
    void test() throws Exception {
        // given
        DBObject objectToSave = BasicDBObjectBuilder.start()
            .add("key", "value")
            .get();

        // when
        mongoTemplate.save(objectToSave, "collection");

        // then
        assertThat(mongoTemplate.findAll(DBObject.class, "collection")).extracting("key")
            .containsOnly("value");
    }
}

Note, that we can quickly create MongoTemplate bean configured to use our manually configured embedded database and register it inside the Spring container by merely creating, e.g., a @TestConfiguration with @Bean method that will return

new MongoTemplate(new MongoClient(bindIp, port), “test”).

More examples can be found on the official Flapdoodle’s GitHub repository.

Logs

We can configure logging messages for MongoDB when running integration tests by adding these two properties to src/test/resources/application.properties file:

logging.level.org.springframework.boot.autoconfigure.mongo.embedded
logging.level.org.mongodb

For example, to disable logging, we simply set the values to off,

logging.level.org.springframework.boot.autoconfigure.mongo.embedded=off
logging.level.org.mongodb=off
Disclaimer

Using embedded database might look like a great idea at the beginning. Indeed, it’s a good approach when we want to test if our application behaves correctly in areas such as:
Object<->Document mapping configuration
Custom persistence lifecycle event listeners (refer to AbstractMongoEventListener)
The logic of any code working directly with the persistence layer

Unfortunately, using an embedded server cannot be considered as “full integration testing”. Flapdoodle’s embedded MongoDB isn’t an official MongoDB product. Therefore, we cannot be sure that it behaves exactly as in the production environment.

Exercise

Task Description:

Environment Setup

MongoDB server is started.
Use mongo shell to connect to your local MongoDB server.
Select the database jdnd-c3.
Checkout the starter code from <>
Import the starter code into the IDE like IntelliJ IDEA CE (Optional).
Modify the PatientRepositoryTest.java class to complete the exercise.

Task List:

Task Feedback:

Nice job on writing a test.